package com.faner.fast.excel.event;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.CellData;
import com.faner.fast.excel.helper.EasyExcelHelper;
import com.faner.fast.excel.kit.ExcelException;
import com.faner.fast.excel.kit.ExcelResult;
import com.faner.fast.excel.kit.ExcelRow;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
public abstract class AnalysisEventAutoListener<T extends ExcelRow> extends AnalysisEventListener<T> {

    // 模板数据
    private List<T> rows = Lists.newArrayList();
    // 模板中错误数据，执行完毕可以选择抛出所有错误 或者 导出错误
    private List<T> errorList = Lists.newArrayList();

    // 设置当前处理的excel实体
    private Class<T> clazz;
    // 设置调用存储数据层接口
    private Function<List<T>, Object> storageDataFun;
    // 设置多少条存储一次数据库
    private int BATCH_COUNT;

    @Setter
    private HttpServletResponse response;

    public AnalysisEventAutoListener(int BATCH_COUNT) {
        this.BATCH_COUNT = BATCH_COUNT;
        this.clazz = this.getClazz();
        this.storageDataFun = this.storageDataFun();
    }

    @Override
    public void onException(Exception exception, AnalysisContext analysisContext) {
        log.error("解析失败，继续解析下一行:{}", exception.getMessage());
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            int perform = excelDataConvertException.getRowIndex() + 1;
            int column = excelDataConvertException.getColumnIndex() + 1;
            log.error("第{}行，第{}列解析异常{}，内容为：{}",
                    perform, column, excelDataConvertException.getCause().getMessage(), excelDataConvertException.getCellData());
        }
    }

    @Override
    public void invokeHead(Map<Integer, CellData> map, AnalysisContext analysisContext) {
        log.info("解析到一条头数据:{}", JSONUtil.toJsonStr(map));
    }

    @Override
    public void invoke(T data, AnalysisContext analysisContext) {

        Integer currentRowNum = analysisContext.getCurrentRowNum();
        //检验
//        this.hibernateValidate(data);
        data.setRowNum(currentRowNum);

        if (StringUtils.isBlank(data.getValidateMessage())) {
            this.rows.add(data);
        } else {
            this.errorList.add(data);
        }

        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (this.rows.size() >= BATCH_COUNT) {
            log.info("解析到{}条数据:{}", BATCH_COUNT, JSONUtil.toJsonStr(this.rows));
            this.checkAndStorageData();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (CollectionUtil.isNotEmpty(this.rows)) {
            this.checkAndStorageData();
        }

        if (CollectionUtil.isNotEmpty(this.errorList)) {
            log.info("导入失败，错误数据有{}条", this.errorList.size());
            if (this.response == null) {
                this.throwValidateMessage();
            }
            this.downloadValidateMessage();
        }
    }

    @Override
    public boolean hasNext(AnalysisContext analysisContext) {
        return true;
    }

    private void downloadValidateMessage() {
        if (this.response != null) {
            EasyExcelHelper.downloadError(this.response, this.clazz, this.errorList);
        }
        this.errorList.clear();
    }

    private void throwValidateMessage() {
        String messages = this.errorList
                .stream()
                .map(e -> String.format("第%s行数据错误；错误原因：【%s】", e.getRowNum(), e.getValidateMessage()))
                .collect(Collectors.joining("\n"));
        this.errorList.clear();
        throw new ExcelException(messages);
    }

//    private void hibernateValidate(T t) {
//        Set<ConstraintViolation<T>> validateSet = HibernateValidator.getValidator().validate(t, Default.class);
//        if (CollectionUtils.isNotEmpty(validateSet)) {
//            StringBuilder result = new StringBuilder();
//            for (ConstraintViolation<T> violation : validateSet) {
//                String message = violation.getMessage();
//                if (StringUtils.isBlank(message)) {
//                    continue;
//                }
//                result.append(message).append(" ");
//            }
//            t.setValidateMessage(result.toString());
//        }
//    }

    private void checkAndStorageData() {
        ExcelResult<T> excelResult = this.checkData(this.rows);
        // 导入数据
        List<T> successList = excelResult.getSuccessList();
        if (CollectionUtil.isNotEmpty(successList)) {
            Object r = this.storageDataFun.apply(this.rows);
            log.info("存储数据完毕，返回结果：{}", r);
        }

        errorList.addAll(excelResult.getErrorList());

        // 存储完成清理 list
        rows.clear();
    }

    public abstract Class<T> getClazz();

    public abstract ExcelResult<T> checkData(List<T> rows);

    protected abstract Function<List<T>, Object> storageDataFun();

}
